#ifndef __INSTRUCTIONS_H
#define __INSTRUCTIONS_H


/*************************/
/* GESTION DES REGISTRES */
/*************************/

/* On appelle les fonctions manipulant les registres en passant des chaînes de caractères,
   ce qui permet d'appeller aussi bien add("esp", "r1") que add("r1", "r1"). Pour faciliter
   le tout, on possède une maccro REGISTER qui passe de 1 à "r1" grâce au tableau définit
   ci-après. */
#define ESP "esp"
#define EBP "ebp"
#define REGISTER(N) G_Registers[N]

#define NB_REGISTERS 16

/* Pour des questions de simplicités (on évite des malloc / free à la pelle, et
   on utilise directement des chaînes de caractères statiques), on crée des tableaux
   contenant des "bouts" d'instruction courramment utilisés (registre, jump, etc... ).
*/
static char const * const G_Registers[NB_REGISTERS]  = {
    "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
    "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
} ;


/*********************/
/* GESTION DES JUMPS */
/*********************/

enum JumpCondType {
    JUMP_EQ = 0x00,
    JUMP_NE = 0x01,
    JUMP_LT = 0x02,
    JUMP_GE = 0x03,
    JUMP_LE = 0x04,
    JUMP_GT = 0x05
} ;

/* Maccro très utile permettant de "retourner" une condition Jump, what ?!
   En gros, on passe de eq (égal) à ne (non égal), ou de lt (plus petit) 
   à ge (plus grand ou égal). Pour simplifier la maccro, on aligne les 
   instructions de tel sorte que les contraires soit toujours à la suite.
   (WHAT ?! En gros, JUMP_NE suit JUMP_EQ, JUMP_GE suit JUMP_LT, etc... , on
   peut donc retrouver le contraire en faisant +1 ou -1 en fonction de la parité). */
#define REVERSE_JUMP(J) ((J)%2 ? ((J) - 1) : ((J) + 1))


/* 
   Ajoute une instruction assembleur. 
   NOTE : Ne devrait être utilisé que pour ajouter les routines __asm__ !
*/
void INS_AddRoutine (const char *routine) ;

/*********************************************************/
/* GESTION DES INSTRUCTIONS N'UTILISANT PAS DE REGISTRES */
/*********************************************************/

/* Ajoute un label (sous la forme label: ). */
void INS_Label (const char *label) ;
void INS_JumpLabel (int nm) ;

/* Ajoute un saut conditionnel, la condition dépend de type. */
void INS_Jump (enum JumpCondType type, int num) ;

/* Ajoute un saut inconditionnel. */
void INS_JumpUC (int num) ;

/* Fait un appel à la primitive call. */
void INS_Call (const char *function) ;
void INS_Leave (void) ;

/****************************************************/
/* GESTION DES INSTRUCTIONS UTILISANT DES REGISTRES */
/****************************************************/

/* Push et Pop */
void INS_Push (const char *reg) ;
void INS_PushX (int firstReg,int lastReg) ;
void INS_Pop (const char *reg) ;
void INS_PopX (int firstReg, int lastReg) ;

/*
  Format de l'argument operande : [DIVAL]+
    - nombre d'opérande => La longueur de la chaine (ex: DVI => 3 opérandes)
    - types d'opérande (D, I, V, A, L => exemple pour 3 opérandes : DDD, DVI, DAI) :
        - D => Accès direct au registre R0
	- I => Accès à l'adresse se trouvant dans le registre [R0]
	- V => Valeur directe #4
	- A => Accès à la valeur se trouvant à l'adresse [#4]
	- L => Un label, i.e une valeur pas encore connue (#label)
*/

enum OperationType {
    OP_CMP,
    OP_ADD,
    OP_SUB,
    OP_MUL,
    OP_DIV,
    OP_LSHIFT,
    OP_RSHIFT,
    OP_AND,
    OP_XOR,
    OP_OR,
    OP_NOT
} ;

/* Ajoute une instruction correspondant à l'opération et aux opérandes spécifiées. */
void INS_Operation (enum OperationType type, const char *operande, ...) ;


/********/
/* MOVs */
/********/

void INS_Mov (const char *ra, const char *rb) ;

void INS_Afc (const char *r, const int val) ;


/****************/
/* LOAD / STORE */
/****************/

/* load RA, [RB] */
void INS_LoadFromReg  (const char *regDest, const char *reg) ;
/* load RA, [RB + offset] */
void INS_LoadFromRegOffset (const char *regDest, const char *reg, const int offset) ;
/* load RA, [#addr] */
void INS_LoadFromAddr (const char *regDest, const int  addr) ;

/* store [RA], RB */
void INS_StoreFromReg  (const char *regSrc, const char *reg) ;
/* store [RA + offset], RB */
void INS_StoreFromRegOffset (const char *regSrc, const char *reg, const int offset) ;
/* store [#addr], RB */
void INS_StoreFromAddr (const char *regSrc, const int  addr) ;


/****************************/
/* GESTION DES COMMENTAIRES */
/****************************/

/* Ajoute un commentaire. */
void INS_Comment (const char *comment) ;


/***********************************/
/* GESTION DU BUFFER D'INSTRUCTION */
/***********************************/

/* NOTE : Le buffer n'est pas actif par défault. */

/* 
   Démarre la mise en buffer des instructions, toutes les instructions
   ajoutés seront mises dans un buffer au lieu d'être ajoutées au fichier.
*/
int startBuffer (void) ;

/* Stop la mise en buffer. */
void stopBuffer (void) ;

/* Inscrit le buffer dans le fichier de sortie. */
int flushBuffer (void) ;


#endif
